﻿#pragma once

#include "helper.h"

// SizeClass类计算对齐后的字节数，方法独立调用，全是static

// 线程申请size的对齐规则：整体控制在最多10%左右的内碎片浪费
// size范围					对齐数				对应哈希桶下标范围
// [1,128]					8B 对齐      		freelist[0,16)
// [128+1,1024]				16B 对齐  			freelist[16,72)
// [1024+1,8*1024]			128B 对齐  			freelist[72,128)
// [8*1024+1,64*1024]		1024B 对齐    		freelist[128,184)
// [64*1024+1,256*1024]		8*1024B 对齐  		freelist[184,208)
class SizeClass
{
public:
	// 返回对齐后的新size：参数1原size，参数2对齐size
	static size_t _RoundUp(size_t size, size_t alignNum)
	{
		return ((size + alignNum - 1) & ~(alignNum - 1));
	}

	// 输出对齐后的新size，size为线程申请的空间大小（9输出16，128输出128，129输出144）
	static size_t RoundUp(size_t size)
	{
		if (size <= 128)
			return _RoundUp(size, 8);
		else if (size <= 1024)
			return _RoundUp(size, 16);
		else if (size <= 8 * 1024)
			return _RoundUp(size, 128);
		else if (size <= 64 * 1024)
			return _RoundUp(size, 1024);
		else if (size <= 256 * 1024)
			return _RoundUp(size, 8 * 1024);
		else
		{
			// 单次申请空间大于256KB，直接按页来对齐
			return _RoundUp(size, 1 << PAGE_SHIFT);
		}
	}

	// 返回某个size的下标（在哪个哈希桶）
	static inline size_t _Index(size_t size, size_t align_shift)
	{
		return ((size + (1 << align_shift) - 1) >> align_shift) - 1;
	}

	// 计算size映射到哪一个哈希桶（ThreadCache和centralCache用，映射规则一样）
	static inline size_t Index(size_t size)
	{
		assert(size <= MAX_BYTES);

		// 每个区间有多少个链
		static int group_array[4] = { 16, 56, 56, 56 };

		if (size <= 128)
			return _Index(size, 3); // 3是指对齐数的二进制位位数，这里8B就是2^3B，所以就是3
		else if (size <= 1024)
			return _Index(size - 128, 4) + group_array[0];
		else if (size <= 8 * 1024)
			return _Index(size - 1024, 7) + group_array[1] + group_array[0];
		else if (size <= 64 * 1024)
			return _Index(size - 8 * 1024, 10) + group_array[2] + group_array[1]
			+ group_array[0];
		else if (size <= 256 * 1024)
			return _Index(size - 64 * 1024, PAGE_SHIFT) + group_array[3] +
			group_array[2] + group_array[1] + group_array[0];
		else
			assert(false);
		return -1;
	}

	// ThreadCache向centralCache单次申请空间的上限块数，返回[2,512]之间（不能太多是为了减少外部碎片）
	static size_t NumMoveSize(size_t size)
	{
		assert(size > 0);

		int num = MAX_BYTES / size;		// 最大分配空间/这次要分配的空间 = 最多分配多少块

		return num = max(2, min(512, num));	// 不能小于2，不能大于512
	}

	// 块页匹配算法，根据块的大小返回需要多少页
	static size_t NumMovePage(size_t size)
	{
		// ThreadCache向CentralCache申请size大小的块时的单次最大申请块数
		size_t num = NumMoveSize(size);

		// num * size就是单次申请最大空间大小
		size_t npage = num * size;

		// 页数 = npage/page大小
		npage >>= PAGE_SHIFT;

		// 算出来0也得给1页
		if (npage == 0)
			npage = 1;

		return npage;
	}
};